/**********************************************************************\

 Copyright 2012 Diggory Blake

 This file is part of klip-engine

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.

\**********************************************************************/

#include "kbufferedstream.h"
#include <string.h>

kBufferedStream::kBufferedStream(kStream* stream, bool owner) : kStream(stream->getOps()) {
	_stream = stream;
	_owner = owner;
	if (stream->canExpand()) {
		_buffer = new kDataBuffer(stream->getSize()/4+64);
		_bufferSize = 0;
	} else {
		_buffer = 0;
		_bufferSize = 0;
	}
}
kBufferedStream::~kBufferedStream() {
	flush();
	delete _buffer;

	if (_owner) {
		delete _stream;
	}
}
size_t kBufferedStream::write(const kDataView& data) {
	if (_buffer) {
		if (!canWrite()) {
			throw kError(_T("Writing to stream is not supported!"));
		}

		if (_bufferSize >= _buffer->getSize()) {
			flush();
		}

		kDataView view = data;
		while (_bufferSize + view.size > _buffer->getSize()) {
			size_t n = _buffer->getSize() - _bufferSize;
			memcpy(_buffer->getPtr() + _bufferSize, view.ptr, n);

			_bufferSize += n;
			view.ptr += n;
			view.size -= n;

			flush();
		}

		memcpy(_buffer->getPtr() + _bufferSize, view.ptr, view.size);
		_bufferSize += view.size;

		if (canMeasure())
			_size = _stream->getSize() + _bufferSize;

		if (canSeek())
			_writeOffset = _stream->tell(keSeek_Write) + _bufferSize;

		return data.size;
	} else {
		size_t result = _stream->write(data);

		if (canMeasure())
			_size = _stream->getSize();

		if (canSeek())
			_writeOffset = _stream->tell(keSeek_Write) + _bufferSize;

		return result;
	}
}
size_t kBufferedStream::read(const kData& data) {
	if (_bufferSize)
		flush();

	size_t result = _stream->read(data);

	if (canSeek())
		_readOffset = _stream->tell(keSeek_Read);

	return result;
}
void kBufferedStream::seek(keSeekMode mode, size_t offset) {
	if (_bufferSize)
		flush();
	_stream->seek(mode, offset);

	_readOffset = _stream->tell(keSeek_Read);
	_writeOffset = _stream->tell(keSeek_Write);
}
void kBufferedStream::flush() {
	if (_bufferSize) {
		if (_stream->write(kDataView(_buffer->getPtr(), _bufferSize)) != _bufferSize) {
			throw kError(_T("Writing to stream failed!"));
		}

		_bufferSize = 0;

		size_t newsize = _stream->getSize()/4+64;
		if (newsize > _buffer->getSize()*2) {
			_buffer->resize(newsize);
		}
	}
	_stream->flush();
}
